#include <vector>
#include <stdlib.h>
#include <time.h>
#include <math.h>

const int compla = 25;
const int param7_pop = 10;
const int param7_iters = 1;
const int param7_randf = 3;
const int param7_randc = 3;
const int param7_cross = 3;
const int param7_randcase = 0;
const int param7_inversions = 3;
const int param7_mutations = 3;
const int param7_mutations_count[] = {3, 3, 3, 1, 1};
const int param7_mutations_bitlen[] = {1, 2, 3, 8, 16};
const int param7_inversions_count[] = {3, 3, 3, 1, 1};
const int param7_inversions_bitlen[] = {1, 2, 3, 8, 16};

const int param6_pop = 10;
const int param6_iters = 3;
const int param6_randf = 3;
const int param6_randc = 3;
const int param6_cross = 3;
const int param6_randcase = 0;
const int param6_inversions = 3;
const int param6_mutations = 1;
const int param6_mutations_count[] = {3};
const int param6_mutations_bitlen[] = {3};
const int param6_inversions_count[] = {3, 3, 3};
const int param6_inversions_bitlen[] = {1, 2, 3};





std::vector<unsigned> funcs6; //6-var functions' population
std::vector<int> weights6; //6-var functions' weights

#include "../NLLibrary.h"

typedef LibraryInfo<5> Lib5;
NLLibrary<Lib5> nllib;

inline int getrandom(int number, int pop) {//gettin' random number from (0,pop-1) with respect to chosen case (number)
    int r = rand();
    switch (number) {
        case 0: return r % pop;
            break;
        case 1: return ((unsigned) (log(.0 + r) / log(.0 + RAND_MAX) * pop)) % pop;
            break;
        case 2: return ((unsigned) ((double) r * (double) r / (double) RAND_MAX / (double) RAND_MAX * (double) pop)) % pop;
            break;
        case 3: return pop - ((unsigned) (sqrt(.0 + r) / sqrt(.0 + RAND_MAX) * pop)) % pop;
            break;
    }
    return r % pop;
}

inline unsigned absmin5(unsigned fr) {
    unsigned s = 8;    
    nllib.get(static_cast<UInt64>(fr), (UInt8&)s);
    return s;
}


inline int add_func6(const unsigned fexp0, const unsigned frem0, const unsigned frem1) {//add new function (fexp0) in population if it's not there
    int w = absmin5(fexp0) + absmin5(fexp0^frem0) + absmin5(fexp0^frem1);
    unsigned i, j;
    for (j = 0; (j < weights6.size()) && (w > weights6[j]); j++);
    for (i = j; (i < weights6.size()) && (w == weights6[i]); i++) if (funcs6[i] == fexp0) return 0;
    if (j < weights6.size()) {
        weights6.insert(weights6.begin() + j, w);
        if (weights6.size() > param6_pop) weights6.erase(weights6.end() - 1);
        funcs6.insert(funcs6.begin() + j, fexp0);
        if (funcs6.size() > param6_pop) funcs6.erase(funcs6.end() - 1);
        return 1;
    }
    return 0;
}

int genmin6(const unsigned rem0, const unsigned rem1, unsigned &pop) {//6-variable function (rem0,rem1) minimization starting with function 'pop'
    unsigned i, j, k, //iterators
            f, //current function
            w, //current weight
            n1, n2, n3, //random numbers
            fl; //flag

    funcs6.resize(0);
    weights6.resize(0);
    //generating population
   
    funcs6.push_back(pop);
    weights6.push_back(absmin5(funcs6[0]) + absmin5(funcs6[0]^rem0) + absmin5(funcs6[0]^rem1));
    for (i = 1; i < param6_pop; i++) {
        if (i < 4)
            switch (i) {
                case 1:f = rem0;
                    break;
                case 2:f = rem1;
                    break;
                case 3:f = rem0^rem1;
                    break;
            } else f = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
        w = absmin5(f) + absmin5(f^rem0) + absmin5(f^rem1);
        for (j = 0; (j < i) && (w > weights6[j]); j++);
        fl = 0;
        {
            weights6.insert(weights6.begin() + j, w);
            funcs6.insert(funcs6.begin() + j, f);
        }
    }

    for (j = 0; j < param6_iters; j++) {//processing iterations
        for (i = 0; i < param6_randf; i++)//random functions
        {
            f = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            w = add_func6(f, rem0, rem1);
        }
        for (i = 0; i < param6_randc; i++)//random crossovers
        {
            n1 = getrandom(param6_randcase, funcs6.size());
            n2 = getrandom(param6_randcase, funcs6.size());
            n3 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            f = (funcs6[n1] & n3)^(funcs6[n2]&(~n3));
            w = add_func6(f, rem0, rem1);
            f = (funcs6[n2] & n3)^(funcs6[n1]&(~n3));
            w = add_func6(f, rem0, rem1);
        }
        for (i = 0; i < param6_cross; i++)//half-to-half crossovers
        {
            n1 = getrandom(param6_randcase, funcs6.size());
            n2 = getrandom(param6_randcase, funcs6.size());
            f = (funcs6[n1]&((unsigned) ((1 << 16) - 1) << 16))^(funcs6[n2] % (1 << 16));
            w = add_func6(f, rem0, rem1);
            f = (funcs6[n2]&((unsigned) ((1 << 16) - 1) << 16))^(funcs6[n1] % (1 << 16));
            w = add_func6(f, rem0, rem1);
        }
        for (k = 0; k < param6_mutations; k++)//mutations
            for (i = 0; i < param6_mutations_count[k]; i++) {
                n1 = getrandom(param6_randcase, funcs6.size());
                f = funcs6[n1];
                if (param6_mutations_bitlen[k] != 32) {
                    n2 = ((unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand()) % ((unsigned) (1 << param6_mutations_bitlen[k]));
                    n3 = rand() % (32 - param6_mutations_bitlen[k]);
                } else {
                    n2 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
                    n3 = 0;
                }
                f = funcs6[n1]^((unsigned) n2 << n3);
                w = add_func6(f, rem0, rem1);
            }
        for (k = 0; k < param6_inversions; k++)//inversions
            for (i = 0; i < param6_inversions_count[k]; i++) {
                n1 = getrandom(param6_randcase, funcs6.size());
                f = funcs6[n1];
                if (param6_inversions_bitlen[k] != 32) {
                    n2 = (unsigned) (1 << param6_inversions_bitlen[k]) - 1;
                    n3 = rand() % (32 - param6_inversions_bitlen[k]);
                } else {
                    n2 = (unsigned) (1 << 31) - 1 + (unsigned) (1 << 31);
                    n3 = 0;
                }
                f = funcs6[n1]^((unsigned) n2 << n3);
                w = add_func6(f, rem0, rem1);
            }

    }
    pop = funcs6[0];
    return weights6[0];
}

inline unsigned min6(const unsigned frem0, const unsigned frem1, unsigned &pop) {//minimize 6-variable function
    //if (useabsmin) return absmin6(rem60,rem61);
    unsigned m = genmin6(frem0, frem1, pop), m1;
    m1 = genmin6(frem0, frem1, pop);
    if (m1 < m) m = m1;
    m1 = genmin6(frem0, frem1, pop);
    if (m1 < m) m = m1;
    return m;
}







struct tf7_exp {
    unsigned f6exp[3];
    unsigned f6rem0, f6rem1;

    bool operator==(tf7_exp other) {
        return ((f6rem0 == other.f6rem0) && (f6rem1 == other.f6rem1));
    }
};

std::vector<tf7_exp> funcs7; //7-var functions' population
std::vector<int> weights7; //7-var functions' weights

inline int add_func7(tf7_exp fexp, const unsigned frem00,
        const unsigned frem01,
        const unsigned frem10,
        const unsigned frem11) {//add new function (fexp) in population if it's not there
    int w = min6(fexp.f6rem0, fexp.f6rem1, fexp.f6exp[0])
            + min6(fexp.f6rem0^frem00, fexp.f6rem1^frem01, fexp.f6exp[1])
            + min6(fexp.f6rem0^frem10, fexp.f6rem1^frem11, fexp.f6exp[2]);
    unsigned i, j;
    for (j = 0; (j < weights7.size()) && (w > weights7[j]); j++);
    for (i = j; (i < weights7.size()) && (w == weights7[i]); i++) if (funcs7[i] == fexp) return 0;
    if (j < weights7.size()) {
        weights7.insert(weights7.begin() + j, w);
        if (weights7.size() > param7_pop) weights7.erase(weights7.end() - 1);
        funcs7.insert(funcs7.begin() + j, fexp);
        if (funcs7.size() > param7_pop) funcs7.erase(funcs7.end() - 1);
        return 1;
    }
    return 0;
}

int genmin7(const unsigned rem00,
        const unsigned rem01,
        const unsigned rem10,
        const unsigned rem11, tf7_exp &pop) {//7-variable function (rem00,rem01,rem10,rem11) minimization starting with function 'pop'
    unsigned i, j, k, //iterators
            w, //current weight
            n1, n2, n3, n4, //random numbers
            fl; //flag
    tf7_exp f, f1; //current functions

    funcs7.resize(0);
    weights7.resize(0);
    //generating population
    funcs7.push_back(pop);
    weights7.push_back(min6(funcs7[0].f6rem0, funcs7[0].f6rem1, funcs7[0].f6exp[0])
            + min6(funcs7[0].f6rem0^rem00, funcs7[0].f6rem1^rem01, funcs7[0].f6exp[0])
            + min6(funcs7[0].f6rem0^rem10, funcs7[0].f6rem1^rem11, funcs7[0].f6exp[0]));

    for (i = 1; i < param7_pop && weights7[0] >= compla; i++) {
        if (i < 4)
            switch (i) {
                case 1:f.f6rem0 = rem00;
                    f.f6rem1 = rem01;
                    break;
                case 2:f.f6rem0 = rem10;
                    f.f6rem1 = rem11;
                    break;
                case 3:f.f6rem0 = rem00^rem10;
                    f.f6rem1 = rem01^rem11;
                    break;
            } else {
            f.f6rem0 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            f.f6rem1 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
        }
        w = min6(f.f6rem0, f.f6rem1, f.f6exp[0])
                + min6(f.f6rem0^rem00, f.f6rem1^rem01, f.f6exp[0])
                + min6(f.f6rem0^rem10, f.f6rem1^rem11, f.f6exp[0]);
        for (j = 0; (j < i) && (w > weights7[j]); j++);
        {
            weights7.insert(weights7.begin() + j, w);
            funcs7.insert(funcs7.begin() + j, f);
        }
    }


    for (j = 0; j < param7_iters && weights7[0] >= compla; j++) {//processing iterations

        for (i = 0; i < param7_randf; i++)//random functions
        {
            f.f6rem0 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            f.f6rem1 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            w = add_func7(f, rem00, rem01, rem10, rem11);
        }
        if (weights7[0] < compla) break;
        for (i = 0; i < param7_randc; i++)//random crossovers
        {
            n1 = getrandom(param7_randcase, funcs7.size());
            n2 = getrandom(param7_randcase, funcs7.size());
            n3 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            n4 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
            f = funcs7[n1];
            f1 = funcs7[n1];
            f.f6rem0 = (funcs7[n1].f6rem0 & n3)^(funcs7[n2].f6rem0 & (~n3));
            f.f6rem1 = (funcs7[n1].f6rem1 & n4)^(funcs7[n2].f6rem1 & (~n4));
            w = add_func7(f, rem00, rem01, rem10, rem11);
            f1.f6rem0 = (funcs7[n2].f6rem0 & n3)^(funcs7[n1].f6rem0 & (~n3));
            f1.f6rem1 = (funcs7[n2].f6rem1 & n4)^(funcs7[n1].f6rem1 & (~n4));
            w = add_func7(f1, rem00, rem01, rem10, rem11);
        }
        if (weights7[0] < compla) break;
        for (i = 0; i < param7_cross; i++)//half-to-half crossovers
        {
            n1 = getrandom(param7_randcase, funcs7.size());
            n2 = getrandom(param7_randcase, funcs7.size());
            f = funcs7[n1];
            f1 = funcs7[n1];
            f.f6rem1 = funcs7[n2].f6rem1;
            w = add_func7(f, rem00, rem01, rem10, rem11);
            f1.f6rem0 = funcs7[n2].f6rem0;
            w = add_func7(f1, rem00, rem01, rem10, rem11);
        }
        if (weights7[0] < compla) break;
        for (k = 0; k < param7_mutations; k++)//mutations
            for (i = 0; i < param7_mutations_count[k]; i++) {
                n1 = getrandom(param7_randcase, funcs7.size());
                f = funcs7[n1];
                if (param7_mutations_bitlen[k] != 32) {
                    n2 = ((unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand()) % ((unsigned) (1 << param7_mutations_bitlen[k]));
                    n3 = rand() % (32 - param7_mutations_bitlen[k]);
                } else {
                    n2 = (unsigned) (rand() + rand())*(unsigned) (rand() + rand()) + rand();
                    n3 = 0;
                }
                if (rand() % 2 == 0) f.f6rem0 = funcs7[n1].f6rem0^((unsigned) n2 << n3);
                else f.f6rem1 = funcs7[n1].f6rem1^((unsigned) n2 << n3);
                w = add_func7(f, rem00, rem01, rem10, rem11);
            }
        if (weights7[0] < compla) break;
        for (k = 0; k < param7_inversions; k++)//inversions
            for (i = 0; i < param7_inversions_count[k]; i++) {
                n1 = getrandom(param7_randcase, funcs7.size());
                f = funcs7[n1];
                if (param7_inversions_bitlen[k] != 32) {
                    n2 = (unsigned) (1 << param7_inversions_bitlen[k]) - 1;
                    n3 = rand() % (32 - param7_inversions_bitlen[k]);
                } else {
                    n2 = (unsigned) (1 << 31) - 1 + (unsigned) (1 << 31);
                    n3 = 0;
                }
                if (rand() % 2 == 0) f.f6rem0 = funcs7[n1].f6rem0^((unsigned) n2 << n3);
                else f.f6rem1 = funcs7[n1].f6rem1^((unsigned) n2 << n3);
                w = add_func7(f, rem00, rem01, rem10, rem11);
            }

    }
    pop = funcs7[0];
    return weights7[0];
}





